//This service uses yastStampLibrary.js

angular.module('kaffipad.services', []).
    factory('KaffipadService', ['$http','$q','$timeout', function($http,$q,$timeout) {
        //Users in this application stored within this service
        var users = [];
        var pollAttempt=0;
        var offline=false;
        //This is an internal function for this service, and should only be called by the internal 'newProjectRequest' function
        var addProject=function(kpUser){
            
            //Creating object for promise generation
            var deferred=$q.defer();
            
            if(!_.isUndefined(kpUser.projectId)){
                deferred.resolve()
                console.log("kaffipad.services.addProject(): "+kpUser + " allready has a projectId, wrong use of this method function abborted");
            }
            else{
                var data = {
                    user: kpUser.stampUser.values.email, 
                    hash:kpUser.stampUser.values.hash,
                    objects:{
                        project:{
                            name:"kaffipad",
                            description:"A project logging coffee consumption",
                            primaryColor:"red",
                            parentId:0
                        }
                    }
                }
                //Creating Yast request
                var newProjectRequest = new YStamp.RequestObject("addData", data);
                var promise=newProjectRequest.request($http);
                
                promise.
                success(
                    function(response){
                        var status = parseInt(response.status);
                        if (status == 0) {
                            //Fetching projectId generated by Yast service
                            var projectId=response.data.objects[0].id;
                            kpUser.projectId=projectId;
                            console.log("kaffipad.services.addProject(): Project added for "+kpUser);
                            deferred.resolve("project added for "+kpUser.getEmail());
                        } 
                        else {
                            deferred.reject("Sync error");
                            console.log("kaffipad.services.addProject(): Failed with status "+status);
                        }
                    }).
                error(
                    function(){
                        deferred.reject("Sync error");
                        console.log("kaffipad.services.addProject(): Failed, could not reach server");
                    })
            }
            //Returning a promise object
            return deferred.promise;
        }
        
        //Returns a error description if the user is not logged in on this application, or the user if it is
        var loggedIn=function(kpUser){
            //Validating user type
            if(kpUser instanceof YStamp.KaffipadUser){
                //Checking if user is in application
                var user=null;
                var email=kpUser.stampUser.values.email;
                for (var i = 0; i < users.length; i++) {
                    if (_.isEqual(users[i].stampUser.values.email, email)) user= users[i];
                }
                if(user!=null){
                    //Checking if user has hash
                    if(user.stampUser.loggedIn()){
                        return user;
                    }
                    else{
                        return user.stampUser.values.email+" is locked, and can not register consumptions";
                    }
                }
                else{
                    return kpUser.stampUser.values.email+" is not added as a user with this applicaiton";
                }
            }
            return ".loggedIn():given " + kpUser+" is not an instance of KaffipadUser";
        }
        
        //This is an internal function for this service, and should only be called with a kaffipadUser which is logged in on this application(valid hash), and has no projectId
        var fetchProjectId=function(kpUser){
            var deferred=$q.defer();
            var data = {
                user:kpUser.stampUser.values.email,
                hash:kpUser.stampUser.values.hash
            }
            //Defining request
            var projectIdRequest = new YStamp.RequestObject(YStamp.RequestObject.prototype.values.validTypes.getProjects, data);
            var promise=projectIdRequest.request($http);
            promise.success(function(response){
                //Code to handle server response
                var status = parseInt(response.status);
                //Code to handle success from Yast service
                if (status == 0) {
                    //Acknowledge server sync
                    var projects=response.data.objects;
                    var length=projects.length;
                    if(length>0){
                        //Find the first(and hopfully only)kaffipad project if it exists, and fetch projectId
                        for(var i=0;i<length;i++){
                            if(_.isEqual(projects[i].project.name,"kaffipad")){
                                kpUser.projectId=projects[i].project.id;
                                console.log("kaddipad.services.fetchProjectId(): added projectId "+projects[i].project.id+" to "+kpUser.stampUser.values.email);
                                break;
                            }
                        }
                    }
                    if(_.isUndefined(kpUser.projectId)){
                        deferred.rejected("No 'kaffipad project found");
                    }
                    else{
                        deferred.resolve("Kaddipad project['"+kpUser.projectId+"'] found");
                    }
                }
                //Code to handle negative response from Yast
                else {
                    console.log("kaffipad.services.fetchProjectId(): failed with status "+status);
                    deferred.reject("Sync error");
                }
            })
            //Code to ajax request failed
            .error(
                function(){
                    console.log("kaddipad.services.fetchProjectId(): could not reach Yast server");
                    deferred.reject("Sync error");
                })
            return deferred.promise;
        }
        
        //A function for parsing returned consumptinos
        var parseConsumptions=function(objects){
            var consumptions=new Array();
            for(var i=0;i<objects.length;i++){
                var record=objects[i].record;
                consumptions.push(new YStamp.KaffipadConsumption(record.variables[0],record.id));
            }
            return consumptions;
        }
        
        var replaceConsumptions=function(kpUser, cookies){
            var deferred=$q.defer();
            var cookieConsumptions = doReadCookieConsumptions(cookies);
            kpUser.synchronizeCookieConsumptions(cookieConsumptions);
            var unsynchedConsumption=kpUser.getUnsynchedConsumption();
            if(unsynchedConsumption.length>0){
                //update failed because there is unsynched consumptions
                deferred.reject("kaffipad.services.replaceConsumption()"+kpUser+" has unsynched consumptions");
            }
            else{
                var data = {
                    user:kpUser.stampUser.values.email,
                    hash:kpUser.stampUser.values.hash,
                    //id:kpUser.projectId, //Waiting for yast fix to single out a project
                    typeId:4
                }
                var dataRequest = new YStamp.RequestObject(YStamp.RequestObject.prototype.values.validTypes.getRecords, data);

                var promise=dataRequest.request($http);
                //Code to handle ajax success
                promise.success(function(response){
                    
                    //Updates the offline variable
                    offline=false;

                    response.status=parseInt(response.status);
                    //Code to handle success from Yast service
                    if (response.status == 0) {
                        //Parses response and adds new consumptions to given user
                        var allConsumptions=parseConsumptions(response.data.objects);
                        kpUser.consumption=allConsumptions;
                        return deferred.resolve(kpUser.getEmail()+", synchronized");
                    } 
                    //Code to handle negative response from Yast
                    else {
                        console.log("kaffipad.services.updateConsumption2(): Failed for user "
                            + kpUser.stampUser.values.email + " : status from Yast:" 
                            + YStamp.RequestObject.prototype.values.yastResponseStatus[response.status]);
                        return deferred.reject(kpUser.getEmail()+", application sync error");
                    }
                }).
                //Code to handle ajax failure
                error(function(){
                    
                    //Updates the offline variable
                    offline=true;
                    
                    console.log("kaddipad.services.updateConsumption2(): Could not reach Yast server");
                    return deferred.reject(kpUser.getEmail()+", could not reach Yast server");
                })
            }
            return deferred.promise;
        }
        
        var buildUserFromCookie = function(email, text) {
            if (text.indexOf("[TYPE:KaffipadUser") < 0) return null;
            var name, hash, avatar;
            var index1, index2;
            index1 = text.indexOf(",NAME:") + 6;
            index2 = text.indexOf(",HASH:");
            if (index1 < 0 || index2 < 0) return null;
            name = text.substring(index1, index2);
            index1 = text.indexOf(",HASH:") + 6;
            index2 = text.indexOf(",AVATAR:");
            if (index1 < 0 || index2 < 0) return null;
            hash = text.substring(index1, index2);
            index1 = text.indexOf(",AVATAR:") + 8;
            index2 = text.indexOf("]END");
            if (index1 < 0 || index2 < 0) return null;
            avatar = text.substring(index1, index2);
            var user = new YStamp.KaffipadUser(new YStamp.User(email, name, avatar));
            user.stampUser.values.hash = hash;
            return user;
        }
        
        var buildConsumptionFromCookie = function(timeStamp, text) {
            if (text.indexOf("[TYPE:Consumption") < 0) return null;
            var id, userEmail;
            var index1, index2;
            index1 = text.indexOf(",ID:") + 4;
            index2 = text.indexOf(",USER:");
            if (index1 < 0 || index2 < 0) return null;
            id = text.substring(index1, index2);
            if (id == "undefined") id = undefined;
            index1 = text.indexOf(",USER:") + 6;
            index2 = text.indexOf("]END");
            if (index1 < 0 || index2 < 0) return null;
            userEmail = text.substring(index1, index2);
            var consumption = new YStamp.KaffipadConsumption(timeStamp, id);
            consumption.setEmail(userEmail);
            return consumption;
        }
        
        var doReadCookieConsumptions = function(cookies) {
            var consumptions = new Array();
            for (var cookie in cookies) {
                var consumption = buildConsumptionFromCookie(cookie, cookies[cookie]);
                if (consumption != null) {
                    consumptions.push(consumption);
                }
            }
            return consumptions;            
        }
        
        return {
            generateUsers: function(size){
                var dummyUsers = new Array();
                for(var i = 0; i < size; i++){
                    var user = new YStamp.User("dummyUser"+i+"@dummy.com", "Dummyuser"+i, "img/users/avatars/" + ((i % 2 == 1) ? "clooney" : "stalone") + ".png");
                    dummyUsers.push(new YStamp.KaffipadUser(user));
                }
                //Adding a user that has a Yast account, with harcoded hash
                var tassoter=new YStamp.KaffipadUser(new YStamp.User("tassoter@gmail.com","Tassoter"));
                dummyUsers.push(tassoter);
                return dummyUsers;
            },
            readCookieUsers: function(cookies) {
                var cookieUsers = new Array();
                for (var cookie in cookies) {
                    var user = buildUserFromCookie(cookie, cookies[cookie]);
                    if (user != null) {
                        cookieUsers.push(user);
                    }
                }
                return cookieUsers;
            },
            readCookieConsumptions: function(cookies) {
                return doReadCookieConsumptions(cookies);
            },
            getCookie: function(cookieStore, key) {
                return cookieStore.get(key);
            },
            putCookie: function(document, key, value, daysUntilExpiration) {
                var rawDocument = document[0];
                var baseElement = document.find('base');
                var href = baseElement.attr('href');
                var cookiePath = href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;
                if (_.isUndefined(daysUntilExpiration)) daysUntilExpiration = -1;
                var expiring;
                if (daysUntilExpiration > 0) {
                    var date = new Date();
                    date.setDate(date.getDate() + daysUntilExpiration);            
                    expiring = date + "";
                    if (expiring.indexOf(" (") > -1) {
                        expiring = expiring.substring(0, expiring.indexOf(" ("));
                    }
                } else {
                    expiring = "Session";
                }
                rawDocument.cookie = escape(key) + "=" + escape(value) + ";path=" + cookiePath + ";expires=" + expiring;
                
            },
            removeCookie: function(cookieStore, key) {
                cookieStore.remove(key);
            },
            buildUserCookie: function(user) {
                if (!(user instanceof YStamp.KaffipadUser)) {
                    return null;
                } else {
                    var text = "[TYPE:KaffipadUser";
                    text += ",NAME:" + user.stampUser.values.name;
                    text += ",HASH:" + user.stampUser.values.hash;
                    text += ",AVATAR:" + user.stampUser.values.image;
                    text += "]END";
                    return text;
                }
            },
            buildConsumptionCookie: function(consumption, userEmail) {
                if (!(consumption instanceof YStamp.KaffipadConsumption)) {
                    return null;
                } else {
                    var text = "[TYPE:Consumption";
                    text += ",ID:" + consumption.id;
                    text += ",USER:" + userEmail;
                    text += "]END";
                    return text;
                }
            },
            getAllUsers: function() {
                return users;
            },
            getUserByEmail: function(email) {
                for (var i = 0; i < users.length; i++) {
                    if (_.isEqual(users[i].stampUser.values.email, email)) return users[i];
                }
                return null;
            },
            lockUser:function(kpUser){
                if(this.loggedIn(kpUser)){
                    kpUser.stampUser.lock();
                }
            },
            unlockUser:function(user,hash){
                if(user instanceof YStamp.KaffipadUser){
                    user=user.stampUser;
                }
                if(_.isUndefined(user) || !(user instanceof YStamp.User || _.isUndefined(hash))){
                    return false;
                }
                user.unlock(hash);
                return true;
            },
            loggedIn:function(kpUser){
                return (loggedIn(kpUser) instanceof YStamp.KaffipadUser);
            },
            isLocked:function(kpUser){
                return !(loggedIn(kpUser) instanceof YStamp.KaffipadUser);
            },
            
            removeUser: function(user) {
                var index = users.indexOf(user);
                users.splice(index, 1);
            },
            lockAllUsers:function(){
                for(var i=0;i<users.length;i++){
                    users[i].stampUser.lock();
                }
            },
            addUser:function(user){
                if(_.isUndefined(user))return false;
                //User is valid if email is found in correct hierarcy...A loosy ceck
                if (_.isUndefined(user.stampUser.values.email)) return false;
                //Checks for duplicates
                if (this.getUserByEmail(user.stampUser.values.email)!=null) return false;
                users.push(user);
                return true;
            },
            addUsers: function(newUsers) {
                if (!_.isArray(newUsers)) return;
                for(var i = 0; i < newUsers.length; i++) {
                    this.addUser(newUsers[i]);
                }
            },
            login:function(user,password){
                var deferred=$q.defer();
                if((user) instanceof YStamp.KaffipadUser){
                    user=user.stampUser;
                    //data object to be sent with request
                    var data = {
                        user: user.values.email, 
                        password: password
                    };
                    var kps=this;
                    var loginRequest = new YStamp.RequestObject("auth.login", data);
                    var loginRequestPromise=loginRequest.request($http);
                    loginRequestPromise.success(
                        function(response){
                            //Parsing the status response from Yast
                            var status = parseInt(response.status);
                            //Unlocking user
                            if (status == 0) {
                                kps.unlockUser(user,response.data.hash);
                                deferred.resolve(user+", unlocked");
                                console.log("kaffipad.services.login(): Login for "+user+", successfull");
                            }
                            //Access denied, password is probably wrong
                            else if(status == 3){
                                defferd.reject(user+", "+YStamp.RequestObject.prototype.values.yastResponseStatus[status])
                            }
                            //Login failed
                            else {
                                console.log("kaffipad.services.login(): Login for " + user + " failed, Yast returned status: " + YStamp.RequestObject.prototype.values.yastResponseStatus[status]);
                                deferred.reject("Login for " + user + " failed, Yast returned status: " + YStamp.RequestObject.prototype.values.yastResponseStatus[status]);
                            }
                        }
                        ).
                    error(
                        function(){
                            console.log("kaffipad.services.login(): Server response failed for "+user);
                        }
                        )
                }
                else{
                    console.log("kaffipad.services.login(): Given user["+user+"] is not a kaffipadUser");
                    deferred.reject("Given user["+user+"] is not a kaffipadUser");
                }
                return deferred.promise;
            },
            addConsumption:function(kpUser,consumption,$cookieStore, $document, $cookies){
                var kpUser=loggedIn(kpUser);
                if(kpUser instanceof YStamp.KaffipadUser){
                    if(_.isUndefined(consumption)){
                        consumption=new YStamp.KaffipadConsumption()
                    }
                    kpUser.consumption.push(consumption);
                    this.updateConsumption(kpUser,$cookieStore, $document, $cookies);
                    return true;
                }
                else{
                    console.log("kaffipad.services.addConsumption():"+kpUser+", can not add consuptions");
                    return false;
                }
            },
            //A function for committing unsynched consumptions
            commitUnsynchedConsumptions:function(kpUser, $cookieStore, $document, $cookies){
                var deferred=$q.defer();
                var kpUser=loggedIn(kpUser);
                var kps = this;
                if(kpUser instanceof YStamp.KaffipadUser){
                    //Reading unsynchronized consumptions from cookies, and synchronizes with app
                    var cookieConsumptions = kps.readCookieConsumptions($cookies);
                    kpUser.synchronizeCookieConsumptions(cookieConsumptions);
                    
                    //Popping all unsynchronized consumtions from user and adding them to the 'unsyncedConsumption' list as Yast records
                    var unsyncedConsumption=kpUser.popUnsynchedConsumption();
                    //Removes cookies
                    for (var i = 0; i < unsyncedConsumption.length; i++) {
                        kps.removeCookie($cookieStore, unsyncedConsumption[i].timestamp);
                    }
                    if(unsyncedConsumption.length>0){
                        //Code to be executed if projectId is available:
                        var commitFunction=function(){
                            //Defining a function to handle consumption synchronization when project id is ready
                            //Putting all unsynchronized consumption into one array of records:
                            var records=new Array();
                            for(var i=0;i<unsyncedConsumption.length;i++){
                                records.push({
                                    record:unsyncedConsumption[i].toRecord(kpUser.projectId)
                                });
                            }
                            var data = {
                                user:kpUser.stampUser.values.email,
                                hash:kpUser.stampUser.values.hash,
                                objects:records
                            }
                            //Creating request with the user email as id to relate asynch response to User
                            var registerRequest = new YStamp.RequestObject(YStamp.RequestObject.prototype.values.validTypes.addData, data);
                            var promise=registerRequest.request($http);
                            //Invalid request object
                            if(promise==false){
                                deferred.reject(kpUser.getEmail()+", Sync error");
                                console.log("kaffipad.services.commitUnsynchedConsumptions():Invalid request object");
                            }
                            else{
                                promise.
                                success(function(response){
                                    var status = parseInt(response.status);
                                    //Printing response status
                                    //Code to handle success from Yast service
                                    if (status == 0) {
                                        //Acknowledge server sync
                                        deferred.resolve(kpUser.getEmail()+", consumptions commited");
                                    }
                                    else{
                                        console.log("kaffipad.services.commitUnsynchedConsumptions(): Failed for user " + kpUser.stampUser.values.email + " : Yast status:" + YStamp.RequestObject.prototype.values.yastResponseStatus[status]);
                                        //Putting the unsychedConsuptions back in user consuption if commit failed
                                        for (var i = 0; i < unsyncedConsumption.length; i++) {
                                            kpUser.consumption.push(unsyncedConsumption[i]);
                                        }
                                        for (var i = 0; i < unsyncedConsumption.length; i++) {
                                            kps.putCookie($document, unsyncedConsumption[i].timestamp, this.buildConsumptionCookie(unsyncedConsumption[i], kpUser.stampUser.values.email), 14);
                                        }
                                        deferred.reject(kpUser.getEmail()+", sync error");
                                    }
                                }).
                                error(function(){
                                    for (var i = 0; i < unsyncedConsumption.length; i++) {
                                        kpUser.consumption.push(unsyncedConsumption[i]);
                                    }
                                    for (var i = 0; i < unsyncedConsumption.length; i++) {
                                        kps.putCookie($document, unsyncedConsumption[i].timestamp, kps.buildConsumptionCookie(unsyncedConsumption[i], kpUser.stampUser.values.email), 14);
                                    }
                                    deferred.reject("Sync error");
                                });
                            }
                        }

                        //Code to acquire projectId if posible
                        if(_.isUndefined(kpUser.projectId)){
                            var promise=fetchProjectId(kpUser);
                            var newPromise=promise.then(
                                function(message){
                                },
                                function(){
                                    var secondDeferred=$q;
                                    //Tries to add a project to user
                                    var addPromise=addProject(kpUser);

                                    addPromise.then(
                                        function(message){
                                            return secondDeferred.resolve(message);
                                        },
                                        function(reason){
                                            return secondDeferred.reject(reason);
                                        })
                                })
                            newPromise.then(
                                function(){
                                    commitFunction();
                                },
                                function(reason){
                                    deferred.reject(kpUser+", sync error");
                                    console.log("kaffipad.services.commitUnsynchedConsuptions2(): Could not add projectId to "+kpUser);
                                })
                        }
                        else{
                            commitFunction();
                        }
                    }
                    else{
                        deferred.resolve("All consumtions for "+kpUser.getEmail()+" is already synchronized");
                    }
                    

                }
                else{
                    deferred.reject("Given user "+kpUser+" was not a caffipadUser");
                }
                return deferred.promise;
            },
            
            //Fetches all consuptions registerd to user at the Yast service, and replaces them whith the current. Tries to register unsynced consuptions if any
            updateConsumption:function(kpUser, $cookieStore, $document, $cookies){
                var deferred=$q.defer();
                var kpUser=loggedIn(kpUser);
                if(kpUser instanceof YStamp.KaffipadUser){
                    var updateFunction=function(){
                        var promise=replaceConsumptions(kpUser, $cookies);
                        promise.then(
                        function(message){
                            deferred.resolve(message);
                            console.log("kaffipad.services.updateConsuption(): update succeded with message: "+message);
                        },
                        function(reason){
                            deferred.reject(reason)
                            console.log("kaffipad.services.updateConsuption(): update failed with reason: "+reason);
                        })
                    }
                    if(kpUser.getUnsynchedConsumption().length>0){
                        var promise=this.commitUnsynchedConsumptions(kpUser, $cookieStore, $document, $cookies);

                        //Defines asynchoronized behaviour for consumption commitment success and failure
                        promise.then(function(message){
                            updateFunction();
                        },
                        function(reason){
                            deferred.reject(reason)
                            console.log("kaffipad.services.updateConsumption(): update failed with reason: "+reason);
                        })
                    }
                    else{
                        updateFunction();
                    }
                    
                }
                else{
                    //loggedIn() did not return a kpUser, but an error message
                    var reason=kpUser;
                    //Returning the error message from loggedIn()
                    deferred.reject(reason);
                    console.log("kaffipad.services.updateConsumption(): update failed with reason: "+reason);
                }
                return deferred.promise;
            },
            updateAllUsers:function($cookieStore, $document, $cookies){
                for(var i=0;i<users.length;i++){
                    var user=users[i];
                    this.updateConsumption(user, $cookieStore, $document, $cookies);
                }
            },
            poll:function($cookieStore, $document, $cookies){
                pollAttempt++;
                this.updateAllUsers($cookieStore, $document, $cookies);
                console.log("poll"+pollAttempt)
            },
            offline:function(){
                return offline;
            }
        }
    }])